package com.citywithincity.reflect;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.damai.action.IAction;

import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * 保存一些
 * 
 * @author Randy
 *
 */
public class CtClassUtil {

	public static CtClass object;
	public static CtClass string;
	public static CtClass map;
	public static CtClass list;
	//httpservletrequest
	public static CtClass requestClass;
	public static ClassPool pool;
	public static CtClass action;
	
	static{
		initClassPath();
	}

	/**
	 * 初始化classpath
	 */
	private static void initClassPath() {
		pool = ClassPool.getDefault();
		Enumeration<URL> dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources("/");
			// 循环迭代下去
			while (dirs.hasMoreElements()) {
				// 获取下一个元素
				URL url = dirs.nextElement();
				// 获取包的物理路径
				String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
				pool.appendClassPath(filePath);
				break;
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (NotFoundException e) {
			throw new RuntimeException(e);
		}

		try {
			pool.appendClassPath(new ClassClassPath(HttpServletRequest.class));
			pool.appendClassPath(new ClassClassPath(CtClassUtil.class));

			object = pool.get(Object.class.getName());
			string = pool.get(String.class.getName());
			map = pool.get(Map.class.getName());
			requestClass = pool.get(HttpServletRequest.class.getName());
			list = pool.get(List.class.getName());
			action = pool.get(IAction.class.getName());
		} catch (NotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	private static final int OFFSET = 0;

	/**
	 * 获取参数名称列表
	 * 
	 * @param method
	 * @return
	 * @throws Exception
	 */
	public static String[] getParams(ClassPool classPool,  Class<?> controllerClass, Method method) throws Exception {
		CtClass cc = classPool.get(controllerClass.getName());
		int len = method.getParameterTypes().length;
		if(len==0){
			return new String[]{};
		}
		CtClass[] types = new CtClass[len];
		Class<?>[] paramTypes = method.getParameterTypes();
		for (int i = 0; i < len; ++i) {
			types[i] = ClassPool.getDefault().get(paramTypes[i].getName());
		}
		CtMethod cm = getDeclaredMethod(cc, method.getName(), types);
		if(cm==null){
			return new String[]{};
		}
		// 使用javaassist的反射方法获取方法的参数名
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		String[] paramNames = new String[len];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < len; i++) {
			paramNames[i] = attr.variableName(i + pos + OFFSET);
		}
		return paramNames;
	}

	public static CtMethod getDeclaredMethod(CtClass cc, String name, CtClass[] types) throws Exception {
		do {
			try {
				return cc.getDeclaredMethod(name, types);
			} catch (NotFoundException e) {
				cc = cc.getSuperclass();
			}

		} while (cc != CtClassUtil.object);
		return null;
	}

	public static CtMethod getDeclaredMethod(CtClass cc, String name) throws Exception {
		do {
			try {
				return cc.getDeclaredMethod(name);
			} catch (NotFoundException e) {
				cc = cc.getSuperclass();
			}

		} while (cc != CtClassUtil.object);
		return null;
	}

	public static void insertClassPath(Class<?> clazz) {
		pool.insertClassPath(new ClassClassPath(clazz));
	}

	public static void implementInterface(CtClass ctClass, Class<?> interfaceClass, String methodBody)
			throws CannotCompileException, NotFoundException { // 获取详细信息
		ctClass.addInterface(ClassPool.getDefault().get(interfaceClass.getName()));

		Method method = interfaceClass.getMethods()[0];
		StringBuilder sb = new StringBuilder();
		sb.append("public abstract ").append(method.getReturnType().getName()).append(" ").append(method.getName())
				.append("(").append(getParamListString(method)).append(");");

		CtMethod ctMethod = CtMethod.make(sb.toString(), ctClass);
		ctMethod.setModifiers(Modifier.PUBLIC);
		ctMethod.setBody(new StringBuilder("{").append(methodBody).append("}").toString());

		ctClass.addMethod(ctMethod);

	}

	public static String getParamListString(Method method) {
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		int i = 0;
		for (Class<?> type : method.getParameterTypes()) {
			if (first) {
				first = false;
			} else {
				sb.append(",");
			}
			sb.append(type.getName()).append(" param").append(i).append(i++);
		}
		return sb.toString();
	}
	public static CtField createField(CtClass targetClass,CtClass fieldClass, String name) throws Exception{
		return createField(targetClass, fieldClass, name,Modifier.PRIVATE);
	}
	public static CtField createField(CtClass targetClass,CtClass fieldClass, String name, int modifier) throws Exception {
		CtField field = CtField.make(new StringBuilder(fieldClass.getName()).append(' ').append(name).append(';').toString(), targetClass);
		field.setModifiers(modifier);
		targetClass.addField(field);
		return field;
	}
	/**
	 * 
	 * @param clazz
	 * @param body
	 * @param modifier
	 * @return
	 * @throws Exception
	 */
	public static CtField createField(CtClass targetClass, String body, int modifier) throws Exception {
		CtField field = CtField.make(body, targetClass);
		field.setModifiers(modifier);
		targetClass.addField(field);
		return field;
	}

	private static void getFields(List<CtField> result, CtClass clazz) {
		CtField[] list = clazz.getDeclaredFields();
		for (CtField field : list) {
			if ((field.getModifiers() & java.lang.reflect.Modifier.STATIC) > 0) {
				continue;
			}
			result.add(field);
		}
	}

	public static CtField[] getFields(CtClass clazz) {
		try {
			List<CtField> result = new ArrayList<CtField>();
			CtClass tmp = clazz;
			do {
				getFields(result, tmp);
				tmp = tmp.getSuperclass();
				if (tmp == object) {
					break;
				}
			} while (true);
			CtField[] list = new CtField[result.size()];
			result.toArray(list);
			return list;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	
	public static CtClass createClass(String name){
		return pool.makeClass(name);
	}
	
	public static void createConstruct(CtClass realClass, String body) throws Exception {
		CtConstructor constructor = new CtConstructor(new CtClass[] {}, realClass);
		constructor.setModifiers(Modifier.PUBLIC);
		constructor.setBody("{" + body + "}");
		realClass.addConstructor(constructor);
	}

	public static CtField addField(CtClass ctClass, Class<?> clazz, String name) throws Exception {
		CtField field = CtField.make("public " + clazz.getName() + " " + name + ";", ctClass);
		field.setModifiers(Modifier.PUBLIC);
		ctClass.addField(field);
		return field;
	}

	public static CtField createField(CtClass clazz, Class<?> fieldClass, String name, int modifier) throws Exception {
		StringBuilder sb = new StringBuilder();
		if ((modifier & Modifier.PUBLIC) > 0) {
			sb.append("public ");
		} else if ((modifier & Modifier.PRIVATE) > 0) {
			sb.append("private ");
		} else if ((modifier & Modifier.PROTECTED) > 0) {
			sb.append("protected ");
		}
		if ((modifier & Modifier.STATIC) > 0) {
			sb.append("static ");
		}

		String fieldName = fieldClass.getName();
		if (fieldName.startsWith("[")) {
			// 数组
			fieldName = "int[]";
		}
		sb.append(fieldName).append(name).append(";\n");
		return createField(clazz, sb.toString(), modifier);
	}

	public static void addStaticArrayField(CtClass ctClass, Class<?> clazz, String name) throws Exception {
		CtField field = CtField.make("public static " + clazz.getName() + "[] " + name + ";", ctClass);
		field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
		ctClass.addField(field);
	}

	public static void addStaticField(CtClass ctClass, Class<?> clazz, String name) throws Exception {
		CtField field = CtField.make("public static " + clazz.getName() + " " + name + ";", ctClass);
		field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
		ctClass.addField(field);
	}

	public static boolean isWapClass(CtClass src, Class<?> dest) {
		try {
			return ((Class<?>) dest.getField("TYPE").get(null)).getName().equals(src.getName());
		} catch (Exception e) {
			return false;
		}
	}

	public static void createMethod(CtClass realClass, String methodName, String body) throws Exception {
		CtMethod method = CtMethod.make("public abstract " + methodName + ";", realClass);
		method.setModifiers(Modifier.PUBLIC);
		method.setBody("{" + body + "}");
		realClass.addMethod(method);
	}

	public static CtClass[] toCtClassTypes(Class<?>[] types) throws NotFoundException {
		int index=0;
		CtClass[] parameters = new CtClass[types.length];
		for (Class<?> type : types) {
			parameters[index++]=pool.get(type.getName());
		}
		return parameters;
	}

}
